<?php
// 应用公共文件

if (!function_exists('exception')) {
    /**
     * 抛出异常处理
     *
     * @param string    $msg  异常消息
     * @param integer   $code 异常代码 默认为0
     * @param string    $exception 异常类
     *
     * @throws Exception
     */
    function exception($msg, $code = 0, $exception = '')
    {
        $e = $exception ?: '\think\Exception';
        throw new $e($msg, $code);
    }
}


/**
 * 全局返回json数据函数-状态,并且强制结束
 */
function retu_json($code = 200, $msg = '' , $data = [])
{
    exit(json_encode(array('code'=>$code,'msg'=>$msg,'data'=>$data)));
}

/**
 * 使用curl get获取远程数据
 * $url     url连接
 * $header  请求头部信息
 */
function curlGet($url,$header=[])
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);                                 //设置访问的url地址
    curl_setopt($ch, CURLOPT_TIMEOUT, 5);                        //设置超时
    curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);   //用户访问代理 User-Agent
    curl_setopt($ch, CURLOPT_REFERER, $_SERVER['HTTP_HOST']);           //设置 referer
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);                //跟踪301
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);                //返回结果
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);            //不验证证书
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);            //不验证证书
    if ($header) {
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
    }
    $r = curl_exec($ch);
    curl_close($ch);
    return $r;
}

/**
 * 商品添加库等 专用
 * 使用curl post获取远程数据
 * $url     请求URL
 * $params  请求参数
 * $header  请求头部信息
 */
function curlPost($url, $params = '', $header = [], $ssl=false)
{
    //$postFields = http_build_query($postFields);
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //不验证证书
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //不验证证书
    if ($header) {
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
    }

    $result = curl_exec($ch);
    curl_close($ch);
    return $result;
}

/**
 * 目前只适用于素材上传图片
 * @param $url 请求的地址
 * @param array $data 传递的数据
 * @param bool $json_encode 是否需要json化
 * @return bool|mixed|string 返回的数据
 */
function curlFormPost($url, $data = [], $json_encode=true) {
    $curl = curl_init(); // 启动一个CURL会话
    curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // 对认证证书来源的检查
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); // 从证书中检查SSL加密算法是否存在
    curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器
    if ($data != null) {
        curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求
        // curl_setopt($curl, CURLOPT_POSTFIELDS, $data); // Post提交的数据包
        if(gettype($data)==="string") {
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }
        else {
            if ($json_encode) {
                curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
            } else {
                curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
            }
        }
    }
    curl_setopt($curl, CURLOPT_TIMEOUT, 300); // 设置超时限制防止死循环
    curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 获取的信息以文件流的形式返回
    $res = curl_exec($curl); // 执行操作
    curl_close($curl);

    $data = json_decode($res, true);
    if($data==NULL) {
        return $res;
    }else {
        return $data;
    }
}



/**
 * 引用获取数据
 * @param $items  原始数据需要键值对的方式返回
 * @return array 数组 也就是递归后的数组
 */
function genTreeData($items) {
    $tree = array(); // 格式化好的树
    foreach ($items as $item)
        if (isset($items[$item['pid']])){
            $items[$item['pid']]['son'][ $item['id'] ] = &$items[$item['id']];
        } else {
            $tree[] = &$items[$item['id']];
        }
    return $tree;
}

/**
 * 判断并生成前缀拼接的url地址  后台-图片资源
 */
function getHostDominUrl($value){
//    \think\facade\Log::write('后台-图片资源','记录');
//    file_put_contents('./1111.log','后台-图片资源',FILE_APPEND);
//    return strpos($value,'http') !== false ? $value : env('app.host_domin').'://'.env('app.static_host').$value;
    return strpos($value,'http') !== false ? $value : env('STATIC.host').$value;
}

/**
 * 判断并生成前缀拼接的url地址  前台api 图片资源
 */
function getApiDominUrl($value){
//    \think\facade\Log::write('前台api','记录');
//    file_put_contents('./1111.log','前台api',FILE_APPEND);
    return strpos($value,'http') !== false ? $value : env('app.host_domin').'://'.env('app.api_static_host').$value;
}

/**
 * 判断文件是否存在本项目
 */
function checkFile($file){
//    \think\facade\Log::write(app()->getRootPath().'public'.$file,'记录');
//    file_put_contents('./1111.log',app()->getRootPath().'public'.$file,FILE_APPEND);
    return file_exists(app()->getRootPath().'public'.$file);
//    if(!file_exists(app()->getRootPath().'public'.$file)) return false;
//    return true;
}

/**
 * 获取app名称
 */
function getAppNmae(){
    return env('app.product_name') ? env('app.product_name') : '美妆说';
}

/**
 * 将一个数组中拿取制定的键值
 */
/**
 * @param array $data 原始数组
 * @param array $keys 需要匹配的键值
 * @param bool|true $bool 等于true 表示拿取 ，否则过滤
 * @return array $all 返回匹配后的数组
 */
function getDataKeys($data = [],$keys = [],$bool = true){
    $all = [];
    foreach($data as $item){
        $linshi = $bool ?  [] : $item;
        foreach($keys as $v){
            // 判断有该字段值
            if($v && isset($item[$v])){
                // 判断是否为拿取还是过滤
                if($bool)
                    $linshi[$v] = $item[$v];
                else
                    unset($linshi[$v]);
            }
        }
        $all[] = $linshi;
    }
    return $all;
}

/**
 * 获取到 删除指定键值后的数组
 * @param array $data 传递过来的数组
 * @param string $key  键值
 * @return array|bool 返回 去除指定字段后的数组  或者false
 */
function unsetDataKeys($data = [], $key = ''){
    if(!$data || !$key || count($data)  == 0)
        return false;
    $keys = is_array($key) ? $key : explode(',',$key);
    foreach($keys as $k){
        if(isset($data[$k]))
            unset($data[$k]);
    }
    return $data;
}

/**
 * Emoji原形转换为String
 * $content 需要转换的字符
 */
function emojiEncode($content)
{
    return json_decode(preg_replace_callback("/(\\\u[ed][0-9a-f]{3})/i", function ($str) {
        return addslashes($str[0]);
    }, json_encode($content)));
}

/**
 * Emoji字符串转换为原形
 * $content 需要转换的字符
 */
function emojiDecode($content)
{
    return json_decode(preg_replace_callback('/\\\\\\\\/i', function () {
        return '\\';
    }, json_encode($content)));
}

/**
 * 转浮点数，保留2位小数
 */
function getformat($num,$nums=100,$compute='/',$mantissa=2)
{
    if($compute == '+') return bcadd(strval($num), strval($nums), $mantissa);
    if($compute == '-') return bcsub(strval($num), strval($nums), $mantissa);
    if($compute == '*') return bcmul(strval($num), strval($nums), $mantissa);
    if($compute == '/') return bcdiv(strval($num), strval($nums), $mantissa);
    return false;
}

/**
 * 写入文件日志
 */
function fileLog($content,$name)
{
    $filename = app()->getRootPath().'pay_log/'.$name;
    $content = $content."\r\n";
    // 文件不存在则创建
    if(!file_exists($filename)){
        $fh = fopen($filename, 'w');
    }else{
        $fh = fopen($filename, 'a');
    }
    if (!$fh) {
        //echo "不能打开文件 $filename";
        return false;
    }
    // 写入内容
    if (fwrite($fh, $content) === FALSE) {
        //echo "不能写入到文件 $filename";
        return false;
    }
    //echo "成功地将 $content 写入到文件 $filename";
    fclose($fh);
    return true;
}

/**
 * 生成订单号  leng 20
 * @param string $prefix 前缀
 * @return string $order_id
 */
function getNumber($prefix = ''){
    @date_default_timezone_set("PRC");
    //订单号码主体（YYYYMMDDHHIISSNNNNNNNN）
    $order_id_main = date('ymdHis') . rand(10000000, 99999999);
    return $prefix.$order_id_main;
    //订单号码主体长度
    $order_id_len = strlen($order_id_main);
    $order_id_sum = 0;
    for ($i = 0; $i < $order_id_len; $i++) {
        $order_id_sum += (int)(substr($order_id_main, $i, 1));
    }
    //唯一订单号码（YYYYMMDDHHIISSNNNNNNNNCC）
    $order_id = $order_id_main . str_pad((100 - $order_id_sum % 100) % 100, 2, '0', STR_PAD_LEFT);
    return $order_id;
}

/**
 * 生成兑换码
 * @param int $leng 长度
 * @return string $code 兑换码
 */
function getCode($leng = 8){
    mt_srand((int)microtime()*10000);
    $charid = strtoupper(md5(uniqid(strval(rand()), true)));
    $uuid = substr($charid, rand(0,32-$leng), $leng);
    return $uuid;
}

/**
 * [imgUpdate TP文件上传]
 * @param    [type]     $fileName [文件变量名]
 * @param    [type]     $fileUrl  [文件路径]
 * @param    [type]     $name    [文件名]
 * @return   [type]               [description]
 */
function imgUpdate($fileName,$fileUrl='images',$name='',$suffix=''):array
{
    //接收文件上传类型
    $type = request()->param('type','','trim');
    if(empty($suffix)){
        list($a, $suf) = explode('/', $_FILES[$fileName]['type']);
        $name = (empty($name)?date('Ymd',time()):$name).'.'.$suf;
    }else $name = (empty($name)?date('Ymd',time()):$name).'.'.$suffix;
    //获取表单上传文件
    $file = request()->file($fileName);
    //组装文件保存目录
    $upload_dir = $fileUrl;
    try {
        //从config/upload.php配置文件中读取允许上传的文件后缀和大小
//        $suffix_config = config('upload.suffix_arr');
//        $size_config = config('upload.size_arr');

//        if (empty($size_config[$type]) || empty($size_config[$type])){
//            return false;
//        }else{
            $ext_arr = array(
                'image' => array('gif','jpg','jpeg','png','bmp'),
                'flash' => array('flv','swf'),
                'video' => array('swf','flv','mp3','wav','wma','wmv','mid','avi','mpg','asf','rm','rmvb','mp4'),
                'file' => array('doc','docx','xls','xlsx','ppt','htm','html','txt','zip','rar','gz','bz2'),
            );
            if(!isset($ext_arr[$type])) return ['code'=>0,'msg'=>'上传文件类型有误'];
            $suffix = $ext_arr[$type];
            if($type == 'video') $size = 100;
            else $size = 8;
//        }
        //验证器验证上传的文件
        validate(['file'=>[
            //限制文件大小
            'fileSize'      =>  $size * 1024 * 1024,
            //限制文件后缀
            'fileExt'       =>  $suffix
        ]],[
            'file.fileSize' =>  '上传的文件大小不能超过'.$size.'M',
            'file.fileExt'  =>  '请上传后缀为:'.implode(',',$suffix).'的文件'
        ])->check(['file'=>$file]);

        //上传文件到本地服务器
        $filename = \think\facade\Filesystem::disk('public')->putFileAs($upload_dir, $file,$name);
        if ($filename){
            $src = config('static.read_img').'/'.$filename;
            return ['code'=>1,'result'=>$src,'msg'=>'成功'];
        }else{
            return ['code'=>0,'msg'=>'上传失败'];
        }
    }catch (\think\exception\ValidateException $e){
        return ['code'=>0,'msg'=>$e->getMessage()];
    }
}


/**
 * 将键值对数据 进行转换读取
 * @param array $arr 数据需要键值对k=>v
 * @param String $key 键值字符串，需要切割成数组
 * @return array|bool 返回新的数组 或者 false
 */
function getSetDefaultValueKeyArrs(Array $arr = [],String $key = '')
{
    if(!$key || count($arr) == 0)
        return false;
    $keys = is_array($key) ? $key : explode(',',$key);
    $allList = [];
    foreach($keys as $k){
        $dV = '';$kV = $k;
        if(strpos($k,'|') > 0)
            list($kV,$dV) = explode('|',$k);
        if(isset($arr[$kV]))
            $dV = $arr[$kV];
        $allList[] = $dV;
    }
    return $allList;
}

/**
 * 截取视频第一帧
 * @param string $file     视频文件
 * @param string $time     第几帧
 * @param string $size    截图尺寸
 * @param string $path    保存地址
 * @return string $path_name   图片地址
 */
function getVideoCover($file,$fileName,$path='/uploads/video/',$time,$size) {
    $m = date('Ym'); //按月份分目录
    $dir = app()->getRootPath() .'public'. $path .$m .'_img/';
    if(!is_dir($dir))  @mkdir($dir,0777); //目录不存在则创建
    $time = $time ? $time : '1';         //默认截取第一秒第一帧
    $info = getVideoInfo($file);
    if(empty($size)){
        $size = ($info['width']??'360') .'*'. ($info['height']??'480');
    }
    if($time > 1){
        if(!empty($info)){
            $time = (int)$info['seconds'] <= $time ? (int)$info['seconds']-1 : $time;
            if($time <= 0) $time = 1;
        }else $time = 1;
    }

    $str = "ffmpeg -i ".$file." -y -f mjpeg -ss ".$time." -t 0.001 -s $size ".$dir.$fileName;
    $out = '';$status = '';
    exec($str,$out,$status);
    return $path.$m .'_img/'.$fileName;
}
/**
 * 获取视频详细信息
 * @param string $file     视频文件
 * @return array $data     视频详细信息
 */
function getVideoInfo($file,$type=0) {
    $command = "ffmpeg -i {$file} 2>&1";

    ob_start();
    passthru($command);
    $info = ob_get_contents();
    ob_end_clean();

    $data = array();
    if (preg_match("/Duration: (.*?), start: (.*?), bitrate: (\d*) kb\/s/", $info, $match)) {
        $data['duration'] = $match[1]; //播放时间
        $arr_duration = explode(':', $match[1]);
        $data['seconds'] = $arr_duration[0] * 3600 + $arr_duration[1] * 60 + $arr_duration[2]; //转换播放时间为秒数
        $data['start'] = $match[2]; //开始时间
        $data['bitrate'] = $match[3]; //码率(kb)
    }

    if (preg_match("/Video: (.*?), (.*?), (.*?)[,\s]/", $info, $match)) {
        $data['vcodec'] = $match[1]; //视频编码格式
        $data['vformat'] = $match[2]; //视频格式
        $data['resolution'] = $match[3]; //视频分辨率
        if(strpos($match[2],'(') !== false){
            //如果存在多个括号则需要重新匹配并截取指定内容
            preg_match("/Video: (.*?), (.*?), (.*?)(?:\)), (.*?)[,\s]/", $info, $match);
            if(!isset($match[4])) return false;
            $data['resolution'] = substr($data['resolution'],0,strrpos($data['resolution'],'('));
            $match[3] = $match[4];
        }
        $arr_resolution = explode('x', $match[3]);
        $data['width'] = $arr_resolution[0];
        $data['height'] = $arr_resolution[1];
    }

    if (preg_match("/Audio: (\w*), (\d*) Hz/", $info, $match)) {
        $data['acodec'] = $match[1]; //音频编码
        $data['asamplerate'] = $match[2]; //音频采样频率
    }

    if (isset($data['seconds']) && isset($data['start'])) {
        $data['play_time'] = $data['seconds'] + $data['start']; //实际播放时间
    }
    $data['size'] = filesize(rtrim($file)); //文件大小

    return $data;
}

/**
 * 迭代文件夹内所有的文件  并 返回
 * @param string $date     文件夹目录
 * @param string $list     空数组即可
 * @param string $prefix     前缀名拼接
 * @return array $data     迭代后的数组信息
 */
function dir_list_file($date,$list = [],$prefix=''){
    //1、首先先读取文件夹
    $temp=scandir($date);
    //遍历文件夹
    foreach($temp as $v){
        $a=$date.'/'.$v;
       if(is_dir($a)){//如果是文件夹则执行
           if($v=='.' || $v=='..'){//判断是否为系统隐藏的文件.和..  如果是则跳过否则就继续往下走，防止无限循环再这里。
               continue;
           }
          $list = array_merge($list,dir_list_file($a,[],$prefix.'/'.$v));//因为是文件夹所以再次调用自己这个函数，把这个文件夹下的文件遍历出来
       }else{
        $list[] = [
            'file' => $a,
            'key' => $prefix .'/' . $v
        ];
       }
    }
    return $list;
}

/**
 * 隐藏用户名 使用星号 匹配
 */
function substr_cut($user_name){
    $strlen = mb_strlen($user_name, 'utf-8');
    $firstStr = mb_substr($user_name, 0, 1, 'utf-8');
    $lastStr = mb_substr($user_name, -1, 1, 'utf-8');
    if($strlen<2) {
        return $user_name;
    }
    else {
        return $strlen == 2 ? $firstStr . str_repeat('*', mb_strlen($user_name, 'utf-8') - 1) : $firstStr . '**' . $lastStr;
        //return $strlen == 2 ? $firstStr . str_repeat('*', mb_strlen($user_name, 'utf-8') - 1) : $firstStr . str_repeat("*", $strlen - 2) . $lastStr;
    }
}

/*
 * 生成token
 */
function createToken($uid){
    $time = time();
    $rand = mt_rand(100,999);
    $token = md5($time.$rand.'jwt-token'.$uid);
    return $token;
}

/*
 * 获取redis实例
 */
function getRedis($select = 0){
    $app = app();
    if(!isset($app->redis)){
        $redis = new \Redis();
        $ret = $redis->connect(config('cache.stores.redis.host'),config('cache.stores.redis.port'));
        if(config('cache.stores.redis.password'))
            $redis->auth(config('cache.stores.redis.password'));
        bind('redis', $redis);
    }else $redis = $app->redis;
    $redis->select($select);
    return $redis;
}